<?php

declare(strict_types=1);

namespace app\admin\service\admin;

use app\admin\model\admin\Api as ApiModel;
use app\admin\model\admin\Menu as MenuModel;
use app\admin\model\admin\MenuApi as MenuApiModel;
use app\admin\model\admin\RoleMenu as RoleMenuModel;
use app\admin\model\admin\UserRole as UserRoleModel;

use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\facade\Cache;
use think\facade\Request;

/**
 * 权限认证服务
 * Class Base
 */
class Auth
{

    // 请求管理类
    protected object $request;

    // 访问地址白名单
    private array $allowapi = [];

    // 登录信息
    private mixed $loginInfo;

    // 用户信息
    private mixed $user;

    // 用户权限url
    private array $apiUrls = [];

    // 生成token的自定义盐
    protected string $token_salt = '';
    // 主键id
    protected string $pks = 'user_id';

    /**
     * 构造方法
     */
    public function __construct()
    {
        // 请求管理类
        $this->request = Request::instance();

        $this->token_salt = app_name() . '_user_login_sgjm';

        // 登录信息
        $this->loginInfo = $this->getLoginInfo();
        // 当前用户信息
        !empty($this->loginInfo) && $this->user = $this->loginInfo['user'];
    }

    /**
     * 获取用户认证Token
     * @param string $key
     * @return bool|string
     */
    private function getToken(string $key = "authorization"): bool|string
    {
        // 获取请求中的token
        $token = request()->header($key);
        // 调试模式下可通过param
        if (empty($token) && is_debug()) {
            $token = request()->param($key);
        }
        // 不存在token报错
        if (empty($token)) {
            return false;
        }
        return $token;
    }
    /**
     * 获取当前登录用户的信息
     * @return mixed
     */
    public function getLoginInfo(): mixed
    {
        if (($token = $this->getToken()) !== false) {
            return Cache::get($token);
        }
        return false;
    }

    /**
     * 获取当前登录用户的ID
     * @return int|null
     */
    public function getLoginUserId(): ?int
    {
        $userInfo = $this->getLoginInfo();
        if (empty($userInfo)) {
            return null;
        }
        return (int) $userInfo['user'][$this->pks];
    }

    /**
     * 记录登录信息
     * @param array $userInfo
     * @param int $d
     * @return bool|string
     */
    public function login(array $userInfo, int $d = 7): bool|string
    {
        // 生成token
        $token = $this->makeToken((int) $userInfo[$this->pks]);
        // 记录缓存, 7天
        Cache::set($token, [
            'user' => $userInfo,
            'is_login' => true,
        ], 86400 * $d);
        return $token;
    }

    /**
     * 清空登录状态
     * @return bool
     */
    public function logout(): bool
    {
        Cache::delete($this->getToken());
        return true;
    }

    /**
     * 更新登录信息
     * @param array $userInfo
     * @return bool
     */
    public function update(array $userInfo): bool
    {
        return Cache::set($this->getToken(), [
            'user' => $userInfo,
            'is_login' => true,
        ], 86400 * 7);
    }

    /**
     * 生成用户认证的token
     * @param int $userId
     * @return string
     */
    protected function makeToken(int $userId): string
    {
        // 生成一个不会重复的随机字符串
        $guid = get_guid_v4();
        // 当前时间戳 (精确到毫秒)
        $timeStamp = microtime(true);
        // 自定义盐
        $salt = $this->token_salt;
        return md5("{$timeStamp}_{$userId}_{$guid}_{$salt}");
    }

    /**
     * 获取当前登录用户菜单权限
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public static function getLoginPermissions(): array
    {
        // 获取当前登录用户的ID
        $userInfo = (new static())->getLoginInfo();

        // 根据当前用户ID获取有权限的菜单列表
        if ($userInfo['user']['is_super']) {
            // 为超级管理员, 拥有所有权限
            $permittedMenuList = static::getPermittedMenuListAll();
        } else {
            // 普通管理员，获取指的权限
            $permittedMenuList = static::getPermittedMenuList((int) $userInfo['user']['user_id']);

        }
        // 生成权限列表
        $permissions = static::buildPermissions($permittedMenuList);
        return [
            // 是否为超级管理员, 拥有所有权限
            'isSuper' => $userInfo['user']['is_super'],
            // 权限列表
            'permissions' => $permissions
        ];
    }

    /**
     * 生成权限列表
     * @param $menuList
     * @return array
     */
    private static function buildPermissions($menuList): array
    {
        $data = [];
        foreach ((array) $menuList as $menu) {
            $item = [
                // 菜单唯一标示
                'path' => $menu['path'],
                // 菜单唯一标示
                'name' => $menu['path'],
                // 菜单隐藏
                'hidden' => $menu['hidden'],
                "meta" => [
                    "title" => $menu['name'],
                    "keepAlive" => $menu['keepAlive'],
                    "icon" => $menu['icon'],
                    // 页面操作项 例如: 新增 编辑 删除
                    "permission" => static::getActionEntitySet($menu),
                    // 访问其他页面时激活本菜单选中
                    'activePath' => $menu['activePath']
                ],
                // 页面文件路径
                'component' => $menu['file_path'],
            ];
            if (isset($menu['children']) && !empty($menu['children'])) {
                $item['children'] = static::buildPermissions($menu['children']);
            }
            $data[] = $item;
            unset($item);
        }
        return $data;
    }

    /**
     * 整理页面操作项
     * @param $menu
     * @return array
     */
    private static function getActionEntitySet($menu): array
    {
        if (!isset($menu['actions']) || empty($menu['actions'])) {
            return [];
        }
        $actionEntitySet = [];
        foreach ($menu['actions'] as $action) {
            $actionEntitySet[] = [
                'title' => $action['name'],
                'icon' => $action['icon'],
                'action' => $action['action_mark'],
            ];

        }
        return $actionEntitySet;
    }

    private static function filterChildrenAction($menuList): array
    {

        $list = [];
        foreach ($menuList as $item) {

            if ($item['type'] != 10)
                continue;

            if (!empty($item['children'])) {
                // 整理actions
                $item['actions'] = array_filter($item['children'], function ($val) {
                    return $val['type'] == 20;
                });
                // 整理children
                $item['children'] && $item['children'] = static::filterChildrenAction($item['children']);
            }

            $list[] = $item;
            unset($item);
        }
        return $list;
    }

    /**
     * 超级管理员获取有权限的菜单列表
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    private static function getPermittedMenuListAll(): array
    {
        // 获取指定角色ID的菜单列表
        $menuList = MenuModel::getAllListByIds();
        //  整理菜单列表的actions
        return static::filterChildrenAction($menuList);
    }

    /**
     * 根据指定用户ID获取有权限的菜单列表
     * @param int $storeUserId
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    private static function getPermittedMenuList(int $storeUserId): array
    {
        // 获取指定用户的所有角色ID
        $roleIds = UserRoleModel::getRoleIdsByUserId($storeUserId);
        // 根据角色ID集获取菜单列表集
        $menuIds = RoleMenuModel::getMenuIds($roleIds);
        // 获取指定角色ID的菜单列表
        $menuList = MenuModel::getListByIds($menuIds);
        //  整理菜单列表的actions
        return static::filterChildrenAction($menuList);
    }


    /**
     * 验证指定url是否有访问权限
     * @param $url
     * @param bool $strict 严格模式($url必须全部有权)
     * @return bool
     */
    public function checkPrivilege($url, bool $strict = true): bool
    {
        if (!is_array($url)) {
            return $this->checkAccess($url);
        }

        foreach ($url as $val) {
            $status = $this->checkAccess($val);
            if ($strict && !$status)
                return false;
            if (!$strict && $status)
                return true;
        }
        return true;
    }
    /**
     * 获取当前用户的权限url列表
     * @return array
     */
    private function getAccessUrls(): array
    {
        if (empty($this->apiUrls)) {
            // 获取当前用户的角色ID集
            $roleIds = UserRoleModel::getRoleIdsByUserId($this->user['user_id']);
            // 获取已分配的菜单ID集
            $menuIds = RoleMenuModel::getMenuIds($roleIds);
            // 获取已分配的API的ID集
            $apiIds = MenuApiModel::getApiIds($menuIds);
            // 获取当前角色所有权限链接
            $this->apiUrls = ApiModel::getApiUrls($apiIds);
        }
        return $this->apiUrls;
    }

    /**
     * 验证url的权限
     * @param $url
     * @return bool
     */
    private function checkAccess($url): bool
    {
        // 访问地址白名单
        $allowApis = $this->allowapi;
        // 验证当前请求是否在白名单
        if (in_array(strtolower($url), array_map('strtolower', $allowApis))) {
            return true;
        }
        // 用户不存在 禁止访问
        if (empty($this->user)) {
            return false;
        }
        // 超级管理员无需验证
        if ($this->user['is_super']) {
            return true;
        }

        // 白名单：通配符支持
        foreach ($allowApis as $action) {
            if (
                strpos($action, '*') !== false
                && preg_match('/^' . str_replace('/', '\/', $action) . '/', $url)
            ) {
                return true;
            }
        }
        // 第一次匹配：获取当前用户的权限url列表
        if (in_array($url, $this->getAccessUrls())) {
            return true;
        }

        // 第二次匹配：获取当前用户的权限url列表，不区分大小写
        if (in_array(strtolower($url), array_map('strtolower', $this->getAccessUrls()))) {
            return true;
        }
        return false;
    }


}
